<?php
/*
PHP Version 5.3+
*/

use function PHPSTORM_META\type;

class Ipf
{

    private $prefStart = array();
    private $prefEnd = array();
    private $fp;
    private $data;
    private $fsize;
    private $mode;
    private $fileName;

    private static $ipv4InstanceLocation = null;
    private static $ipv4InstanceScenes = null;
    private static $ipv4InstanceProxy = null;

    private function __construct($path, $mode)
    {
        $this->loadFile($path, $mode);
    }

    public static function getIpv4InstanceLocationFile($path = null)
    {
        if ($path === null) {
            echo "please input database path";
            return;
        }
        return new self($path, "file");
    }
    public static function getIpv4InstanceLocationMemory($path = null)
    {
        if (self::$ipv4InstanceLocation === null) {
            if ($path === null) {
                echo "please input database path";
                return;
            }
            self::$ipv4InstanceLocation = new self($path, "mem");
        }
        return self::$ipv4InstanceLocation;
    }
    public static function getIpv4InstanceScenesFile($path = null)
    {
        if ($path === null) {
            echo "please input database path";
            return;
        }
        return new self($path, "file");
    }
    public static function getIpv4InstanceScenesMemory($path = null)
    {
        if (self::$ipv4InstanceScenes === null) {
            if ($path === null) {
                echo "please input database path";
                return;
            }
            self::$ipv4InstanceScenes = new self($path, "mem");
        }
        return self::$ipv4InstanceScenes;
    }
    public static function getIpv4InstanceProxyFile($path = null)
    {
        if ($path === null) {
            echo "please input database path";
            return;
        }
        return new self($path, "file");
    }
    public static function getIpv4InstanceProxyMemory($path = null)
    {
        if (self::$ipv4InstanceProxy === null) {
            if ($path === null) {
                echo "please input database path";
                return;
            }
            self::$ipv4InstanceProxy = new self($path, "mem");
        }
        return self::$ipv4InstanceProxy;
    }

    private function loadFile($path, $mode)
    {
        $this->fp = fopen($path, 'rb');
        $this->fsize = filesize($path);
        $this->mode = $mode;
        $this->fileName = $path;

        if ($mode == "file") {
            for ($k = 0; $k < 256; $k++) {
                $i = $k * 8 + 4;
                fseek($this->fp, $i);
                $dataStr = fread($this->fp, 8);
                $this->prefStart[$k] = $this->BytesToLong($dataStr[0], $dataStr[1], $dataStr[2], $dataStr[3]);
                $this->prefEnd[$k] = $this->BytesToLong($dataStr[4], $dataStr[5], $dataStr[6], $dataStr[7]);
            }
        } else {
            $this->data = fread($this->fp, $this->fsize);
            for ($k = 0; $k < 256; $k++) {
                $i = $k * 8 + 4;
                $this->prefStart[$k] = $this->BytesToLong($this->data[$i], $this->data[$i + 1], $this->data[$i + 2], $this->data[$i + 3]);
                $this->prefEnd[$k] = $this->BytesToLong($this->data[$i + 4], $this->data[$i + 5], $this->data[$i + 6], $this->data[$i + 7]);
            }

        }
    }

    private function getByCur($i)
    {
        $p = 2052 + (intval($i) * 9);
        if ($this->mode == "file") {
            fseek($this->fp, $p);
            $dataStr = fread($this->fp, 9);
            $offset = $this->BytesToLong($dataStr[4], $dataStr[5], $dataStr[6], $dataStr[7]);
            $length = ord($dataStr[8]);
            fseek($this->fp, $offset);
            return fread($this->fp, $length);
        }
        $offset = $this->BytesToLong($this->data[$p + 4], $this->data[$p + 5], $this->data[$p + 6], $this->data[$p + 7]);
        $length = ord($this->data[$p + 8]);
        return substr($this->data, $offset, $length);
    }

    function __destruct()
    {
        if ($this->fp !== NULL) {
            fclose($this->fp);
        }
    }

    function get($ip)
    {
        if (filesize($this->fileName) !== $this->fsize) {
            $this->loadFile($this->fileName, $this->mode);
        }
        $val = sprintf("%u", ip2long($ip));
        $ip_arr = explode('.', $ip);
        $pref = $ip_arr[0];
        $low = $this->prefStart[$pref];
        $high = $this->prefEnd[$pref];
        $cur = $low == $high ? $low : $this->Search($low, $high, $val);
        if ($cur == 100000000) {
            return "无信息";
        }
        return $this->getByCur($cur);
    }

    function Search($low, $high, $k)
    {
        $M = 0;
        while ($low <= $high) {
            $mid = floor(($low + $high) / 2);
            $p = 2052 + ($mid * 9);
            if ($this->mode == "file") {
                fseek($this->fp, $p);
                $dataStr = fread($this->fp, 9);
                $endipNum = $this->BytesToLong($dataStr[0], $dataStr[1], $dataStr[2], $dataStr[3]);
            } else {
                $endipNum = $this->BytesToLong($this->data[(int) $p], $this->data[(int) $p + 1], $this->data[(int) $p + 2], $this->data[(int) $p + 3]);
            }
            if ($endipNum >= $k) {
                $M = $mid;
                if ($mid == 0) {
                    break;
                }
                $high = $mid - 1;
            } else
                $low = $mid + 1;
        }
        return $M;
    }

    function BytesToLong($a, $b, $c, $d)
    {
        $iplong = (ord($a) << 0) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24);
        if ($iplong < 0) {
            $iplong += 4294967296;
        }
        ;
        return $iplong;
    }

    public function upload($remoteFile, $localFile)
    {
        $content = file_get_contents($remoteFile);
        if ($content !== false) {
            file_put_contents($localFile, $content);
            echo "download success {$localFile}";
        } else {
            echo "download failed";
        }
        $zip = new ZipArchive;
        $res = $zip->open($localFile); // 打开ZIP文件  
        if ($res === TRUE) {
            // 解压到当前目录  
            $zip->extractTo('./');
            $zip->close();
            echo 'extract file success';
        } else {
            echo 'extract file failed';
        }
    }
}
